/* 
 * File:   randomtestcard.c
 * Author: Arnold Baghomian
 ** Class: CS362
 * Assignment 4
 * Created on July 21, 2014, 12:49 PM
 */
#define _GNU_SOURCE
#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "rngs.h"
#include <math.h>
//define DUMP 
int count = 0;

/*
 * 
 */  
//void zzzconsoleroomcardplay(int i, int currentPlayer, int handPos,
//		struct gameState* state)

void dumpState(FILE* fp, struct gameState *g, char* s)
{
    int i,j;
    fprintf(fp,"\n");
    fprintf(fp,"%s\n", s);
    fprintf(fp,"dump of game state:\n");
    fprintf(fp,"\n");
    fprintf(fp,"number of players %d\n", g->numPlayers);
    fprintf(fp,"       whose turn %d\n", g->whoseTurn);
    fprintf(fp,"            coins %d\n", g->coins);
    fprintf(fp,"number of actions %d\n", g->numActions);
    fprintf(fp,"   number of buys %d\n", g->numBuys);
    fprintf(fp,"   outpost played %d\n", g->outpostPlayed);
    fprintf(fp,"     outpost turn %d\n", g->outpostTurn);
    fprintf(fp,"            phase %d\n", g->phase);
    fprintf(fp,"played card count %d\n", g->playedCardCount);
    //fprintf(fp,"\n");
    fprintf(fp,"\nsupply count: ");
    for (i=0;i<treasure_map+1;++i)
        //fprintf(fp,"     supply count[%d] = %d\n",  i, (g->supplyCount[i]));  
        fprintf(fp,"%d, ", (g->supplyCount[i]));  

    fprintf(fp,"\n");
    fprintf(fp,"\nembargo tokens: ");
    for (i=0;i<treasure_map+1;++i)
        //fprintf(fp,"    embargo tokens[%d] = %d\n", i, (g->embargoTokens[i]));  
        fprintf(fp,"%d, ", (g->embargoTokens[i]));  
    fprintf(fp,"\n");
    fprintf(fp,"\nplayed card: ");
   for (j=0; j < g->playedCardCount; j++)
          //fprintf(fp,"    played card[%d] = %d\n", j, (g->playedCards[j]));    
          fprintf(fp,"%d, ",(g->playedCards[j]));    
    
    
    for (i = 0; i < g->numPlayers; ++i)
    {
        fprintf(fp,"\nPlayer %d\n",i);
        fprintf(fp,"   handcount %d\n", g->handCount[i]);    
        fprintf(fp,"   deckcount %d\n", g->deckCount[i]);    
        fprintf(fp,"discardcount %d\n", g->discardCount[i]);  
        fprintf(fp,"\nhand: ");
        for (j=0; j < g->handCount[i]; j++)
          //fprintf(fp,"           hand[%d] = %d\n", j, g->hand[i][j]);    
            fprintf(fp,"%d, ", g->hand[i][j]);    
        fprintf(fp,"\ndeck: ");
        for (j=0; j < g->deckCount[i]; j++)
          //fprintf(fp,"           deck[%d] = %d\n", j, g->deck[i][j]);    
            fprintf(fp,"%d, ", g->deck[i][j]);    
        fprintf(fp,"\ndiscard: ");
        for (j=0; j < g->discardCount[i]; j++)
          //fprintf(fp,"        discard[%d] = %d\n", j, g->discard[i][j]);                
          fprintf(fp,"%d, ", g->discard[i][j]);                
    }
    fprintf(fp,"\nEnd of dump\n");
    fflush(fp); 
}

void draw1Card(int p, struct gameState *pre, struct gameState *g) {

  //printf ("drawCard POST: p %d HC %d DeC %d DiC %d\n",
  //      p, g->handCount[p], g->deckCount[p], g->discardCount[p]);

  if (pre->deckCount[p] > 0) {
    pre->handCount[p]++;
    pre->hand[p][pre->handCount[p]-1] = pre->deck[p][pre->deckCount[p]-1];
    pre->deckCount[p]--;
  } else if (pre->discardCount[p] > 0) {
    memcpy(pre->deck[p], g->deck[p], sizeof(int) * pre->discardCount[p]);
    memcpy(pre->discard[p], g->discard[p], sizeof(int)*pre->discardCount[p]);
    pre->hand[p][g->handCount[p]-1] = g->hand[p][g->handCount[p]-1];
    pre->handCount[p]++;
    pre->deckCount[p] = pre->discardCount[p]-1;
    pre->discardCount[p] = 0;
  }

}

void use4discards(int p, struct gameState* pre, struct gameState* g) {
    assert(pre->discardCount[p]>3);

    // we have used up all 2 cards in deck now it is reshuffled
    // shuffle is random so take from new deck
    memcpy(pre->deck[p], g->deck[p], sizeof (int) * pre->discardCount[p]); // a shuffle happened, assume shuffle works and copy the new deck
    memcpy(pre->discard[p], g->discard[p], sizeof (int)*pre->discardCount[p]); // make sure even unused positions in discard pile match
    pre->hand[p][g->handCount[p] - 1] = g->hand[p][g->handCount[p] - 1];
    pre->hand[p][g->handCount[p] - 2] = g->hand[p][g->handCount[p] - 2];
    pre->hand[p][g->handCount[p] - 3] = g->hand[p][g->handCount[p] - 3];
    pre->hand[p][g->handCount[p] - 4] = g->hand[p][g->handCount[p] - 4];
    pre->discardCount[p] = 0; //reshuffled
    pre->handCount[p] += 4; //addded 3 more cards
    pre->deckCount[p] = pre->discardCount[p] - 4; // size of deck decreased by three

}

void use3discards(int p, struct gameState* pre, struct gameState* g) {
    assert(pre->discardCount[p]>2);

    memcpy(pre->deck[p], g->deck[p], sizeof (int) * pre->discardCount[p]); // a shuffle happened, assume shuffle works and copy the new deck
    memcpy(pre->discard[p], g->discard[p], sizeof (int)*pre->discardCount[p]); // make sure even unused positions in discard pile match
    pre->hand[p][g->handCount[p] - 1] = g->hand[p][g->handCount[p] - 1];
    pre->hand[p][g->handCount[p] - 2] = g->hand[p][g->handCount[p] - 2];
    pre->hand[p][g->handCount[p] - 3] = g->hand[p][g->handCount[p] - 3];
    pre->discardCount[p] = 0; //reshuffled
    pre->handCount[p] += 3; //addded 3 more cards
    pre->deckCount[p] = pre->discardCount[p] - 3; // size of deck decreased by three

}
void use2discards(int p, struct gameState* pre, struct gameState* g) {

    assert(pre->discardCount[p]>1);
        memcpy(pre->deck[p], g->deck[p], sizeof (int) * pre->discardCount[p]); // a shuffle happened, assume shuffle works and copy the new deck
        memcpy(pre->discard[p], g->discard[p], sizeof (int)*pre->discardCount[p]); // make sure even unused positions in discard pile match
        pre->hand[p][g->handCount[p] - 1] = g->hand[p][g->handCount[p] - 1];
        pre->hand[p][g->handCount[p] - 2] = g->hand[p][g->handCount[p] - 2];
        pre->discardCount[p] = 0; //reshuffled
        pre->handCount[p] += 2; //added 2 more cards
        pre->deckCount[p] = pre->discardCount[p] - 2; // size of deck decreased by two
}

void use1discards(int p, struct gameState* pre, struct gameState* g) {

    assert(pre->discardCount[p]>0);
        memcpy(pre->deck[p], g->deck[p], sizeof (int) * pre->discardCount[p]); // a shuffle happened, assume shuffle works and copy the new deck
        memcpy(pre->discard[p], g->discard[p], sizeof (int)*pre->discardCount[p]); // make sure even unused positions in discard pile match
        pre->hand[p][g->handCount[p] - 1] = g->hand[p][g->handCount[p] - 1];
        pre->discardCount[p] = 0; //reshuffled
        pre->handCount[p]++; //add one more card
        pre->deckCount[p] = pre->discardCount[p] - 1; // size of deck decreased by one
}

int zzzconsoleroomcardplaytest(int iter, int p, int handPos, struct gameState *g, FILE* fp) {
    int i; //for loops
    int numDiscards;
    int numP = g->numPlayers;
    assert(numP > 0 && numP < 5); // check number of players
    struct gameState preState;
    struct gameState *pre = &preState;
    memcpy(pre, g, sizeof (struct gameState)); // copy the games state before for comparison
#ifdef DUMP
    dumpState(fp, pre,"   before call\n");
#endif    
    zzzconsoleroomcardplay(i, p, handPos, g); // play this card
    //need also to correct for size of discard pile!
    if (pre->deckCount[p] > 3) {
        pre->handCount[p] += 4;
        pre->hand[p][pre->handCount[p] - 4] = pre->deck[p][pre->deckCount[p] - 1];
        pre->hand[p][pre->handCount[p] - 3] = pre->deck[p][pre->deckCount[p] - 2];
        pre->hand[p][pre->handCount[p] - 2] = pre->deck[p][pre->deckCount[p] - 3];
        pre->hand[p][pre->handCount[p] - 1] = pre->deck[p][pre->deckCount[p] - 4];
        pre->deckCount[p] -= 4;
        
    } else if (pre->deckCount[p] == 3) {
        pre->handCount[p] += 3;
        pre->hand[p][pre->handCount[p] - 3] = pre->deck[p][pre->deckCount[p] - 1];
        pre->hand[p][pre->handCount[p] - 2] = pre->deck[p][pre->deckCount[p] - 2];
        pre->hand[p][pre->handCount[p] - 1] = pre->deck[p][pre->deckCount[p] - 3];
        pre->deckCount[p] -= 3;
        // we have used up all three cards in deck now it is reshuffled
        if (pre->discardCount[p]>0)
            use1discards(p,pre,g);
        
    } else if (pre->deckCount[p] == 2) {
        pre->handCount[p] += 2;
        pre->hand[p][pre->handCount[p] - 2] = pre->deck[p][pre->deckCount[p] - 1];
        pre->hand[p][pre->handCount[p] - 1] = pre->deck[p][pre->deckCount[p] - 2];
        pre->deckCount[p] -= 2;
        // we have used up all three cards in deck now it is reshuffled
        if (pre->discardCount[p]>1)
            use2discards(p, pre, g);
        else if (pre->discardCount[p]>0)
            use1discards(p,pre,g);
        
    } else if (pre->deckCount[p] == 1) {



        pre->handCount[p] += 1;
        pre->hand[p][pre->handCount[p] - 1] = pre->deck[p][pre->deckCount[p] - 1]; // use last discard
        pre->deckCount[p] -= 1;

        if (pre->discardCount[p] > 2)
            use3discards(p, pre, g);
        else if (pre->discardCount[p]>1)
            use2discards(p, pre, g);
        else if (pre->discardCount[p]>0)
            use1discards(p,pre,g);

        
    } else if (pre->deckCount[p] == 0) {


        if (pre->discardCount[p] > 3)
            use4discards(p, pre, g);
        else if (pre->discardCount[p] > 2)
            use3discards(p, pre, g);
        else if (pre->discardCount[p]>1)
            use2discards(p, pre, g);
        else if (pre->discardCount[p]>0)
            use1discards(p,pre,g);
    }

    
    //in all cases discard the used card now and add one to number of buys
        pre->numBuys++;
        discardCard(handPos, p, pre, 0); // discard the card we are not testing this function
        //pre->discard[p][pre->discardCount[p]] = council_room; // discarded card
        //pre->playedCard[]=council_room; //played card
        //pre->discardCount[p]++;
    // now need to check other players draw card function
        for (i=0; i<numP; ++i)
        {
            if (i==p) continue; // don't do current player
               draw1Card(i,pre,g); // changes pre for drawing one card
        }
    // does it check out?
#ifdef DUMP
    dumpState(fp, pre,"adjusted before call\n");
    dumpState(fp, g,"after call\n");
    if(memcmp(pre, g, sizeof (struct gameState)) == 0)
        fprintf(fp,"\nAssert true\n");
    else
        fprintf(fp,"\nAssert failed\n");
#endif
//assert(memcmp(pre, g, sizeof (struct gameState)) == 0);
    if(memcmp(pre, g, sizeof (struct gameState)) != 0)
    {
        count++;
        printf("Iteration %d assert failed.\n", iter);
    }
}

  struct gameState G;

int main() {

    int i, j, n, r, p, deckCount, discardCount, handCount, players, handPos;

    int k[10] = {adventurer, council_room, feast, gardens, mine,
        remodel, smithy, village, baron, great_hall};

        FILE * fp;

#ifdef DUMP
    fp = fopen ("randomtestcard.log","w");
#endif
    printf("Testing council_room card.\n");

    printf("RANDOM TESTS.\n");

    SelectStream(2);
    //PutSeed(3);
    PutSeed(5);
    int trials = 2000;
     for (n = 0; n < trials; n++) {   
    
        for (j=0; j< MAX_DECK; j++)
            {
                G.playedCards[j] = floor(Random() * (treasure_map+1));
            }   
        for (i=0; i < MAX_PLAYERS; i++)
        {
            for (j=0; j< MAX_HAND; j++)
                G.hand[i][j] = floor(Random() * (treasure_map+1));
            for (j=0; j< MAX_DECK; j++)
            {
                G.deck[i][j] = floor(Random() * (treasure_map+1));
                G.discard[i][j] = floor(Random() * (treasure_map+1));
            }   
        }
        for(i = 0; i < treasure_map+1; i++)
        {
            G.supplyCount[i] = floor (Random()*100);
            G.embargoTokens[i] = floor (Random()*100);
        }
        players = floor(Random()* 3) + 2; // 2 - 4 payers allowed
        G.numPlayers = players;
        //set random deck sizes for each player
        for (i = 0; i < players; ++i) {
            G.deckCount[i] = floor(Random() * MAX_DECK);
            G.discardCount[i] = floor(Random() * MAX_DECK);
            G.handCount[i] = floor(Random() * MAX_HAND);
        }
        
        p = floor(Random() * players); // pick a random player
        handPos = floor(Random() * G.handCount[p]); // pick a random card in hand
        G.whoseTurn = p;
        G.coins = floor(Random()*200);
        G.numActions = floor(Random()*100);
        G.numBuys = floor(Random()*100);
        G.outpostPlayed   = floor(Random()*100);
        G.outpostTurn = floor(Random()*100)  ;  // not used in code
        G.phase = floor(Random()*2);   // not used in code
        G.playedCardCount = floor(Random()*100);
    


        G.hand[p][handPos] = council_room; // set card to council room
        zzzconsoleroomcardplaytest( n, p, handPos, &G, fp);

    }

    printf("\nTotal trials %d, total failed %d\n", trials, count);
#ifdef DUMP
    fclose(fp);
#endif
   return 0;

}